﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;

using DG.Tweening;
using Path = DG.Tweening.Plugins.Core.PathCore.Path;

namespace linkouqs.Gamekit {

    [RequireComponent(typeof(BoxCollider2D))]
    [RequireComponent(typeof(Rigidbody2D))]
    [RequireComponent(typeof(CharacterInput))]
    public class CharacterController2D : MonoBehaviour {
        /// <summary>
        /// 角色索引
        /// </summary>
        public int index;
        /// <summary>
        /// 角色移动速度
        /// </summary>
        [Range(0f, 10f)]
        public float runSpeed = 3.5f;
        /// <summary>
        /// 跳跃速度
        /// </summary>
        float m_JumpSpeed;
        /// <summary>
        /// 最大跳跃高度
        /// </summary>
        [Range(0f, 10f)]
        public float jumpHeight = 3;
        /// <summary>
        /// 最小跳跃高度占最大跳跃高度的百分比
        /// </summary>
        [Range(0.1f, 1f)]
        public float minHeigthPercent = 0.66f;
        /// <summary>
        /// 重力加速度
        /// </summary>
        [Range(0.1f, 100)]
        public float gravity = 30f;
        /// <summary>
        /// 被忽略的重力加速度
        /// </summary>
        float m_GravityIgnore;
        /// <summary>
        /// 检测顶部的层
        /// </summary>
        public LayerMask ceilingLayers;
        /// <summary>
        /// 检测地面的层
        /// </summary>
        public LayerMask groundLayers;
        /// <summary>
        /// 每个方向检测碰撞时说发射的射线数量
        /// </summary>
        [Range(3, 12)]
        public int raycastCount = 3;
        /// <summary>
        /// 射线检测距离
        /// </summary>
        [Range(0.0001f, 0.1f)]
        public float raycastDistance = 0.001f;
        /// <summary>
        /// 是否在地面
        /// </summary>
        public bool IsGround { get; private set; }
        /// <summary>
        /// 是否到达顶部
        /// </summary>
        public bool IsCeiling { get; private set; }
        /// <summary>
        /// 角色是否向左
        /// </summary>
        public bool IsLeft { get; private set; }
        /// <summary>
        /// 是否正在跳跃
        /// </summary>
        public bool IsJumping {
            get {
                return !IsGround && m_Velocity.y > 0f;
            }
        }
        /// <summary>
        /// 是否正在下落
        /// </summary>
        public bool IsFalling {
            get {
                return !IsGround && m_Velocity.y < 0f;
            }
        }

        public Animator Animator { get { return m_Animator; } }
        public Rigidbody2D Rigidbody2D { get { return m_Rigidbody2D; } }
        public BoxCollider2D BoxCollider2D { get { return m_BoxCollider2D; } }
        public CharacterInput CharacterInput { get { return m_CharacterInput; } }
        public CharacterSlot CharacterSlots { get { return m_CharacterSlots; } }

        readonly List<Collider2D> m_GroundColliders = new List<Collider2D>();
        readonly List<Collider2D> m_CeilingColliders = new List<Collider2D>();

        Animator m_Animator;
        Rigidbody2D m_Rigidbody2D;
        BoxCollider2D m_BoxCollider2D;
        CharacterInput m_CharacterInput;
        CharacterSlot m_CharacterSlots;

        Vector2 m_Velocity = Vector2.zero;
        Vector2 m_PreviousPosition;
        Vector2 m_CurrentPosition;
        Vector2 m_NextMovement;

        bool m_DisableGravity;

        public CharacterState CurrentState { get; set; }

        public bool LockMovement { get; set; }

        public bool LockDirection { get; set; }

        void Awake() {
            m_Animator = GetComponent<Animator>();
            m_Rigidbody2D = GetComponent<Rigidbody2D>();
            m_BoxCollider2D = GetComponent<BoxCollider2D>();
            m_CharacterInput = GetComponent<CharacterInput>();
            m_CharacterSlots = GetComponent<CharacterSlot>();

            m_CurrentPosition = m_Rigidbody2D.position;
            m_PreviousPosition = m_Rigidbody2D.position;
            Physics2D.queriesStartInColliders = false;
            CaculateJumpSpeed();
        }

        void CaculateJumpSpeed() {
            m_JumpSpeed = Mathf.Sqrt(2f * jumpHeight * minHeigthPercent * gravity);
            m_GravityIgnore = gravity - (0.5f * m_JumpSpeed * m_JumpSpeed / jumpHeight);
        }

        void Start() {
            CharacterManager.Instance.RegistCharacter(this);
        }

        /// <summary>
        /// 下一次FixedUpdate时移动指定的距离
        /// </summary>
        public void Move(Vector2 movement) {
            m_NextMovement += movement;
        }

        /// <summary>
        /// 被角色直接转移到指定位置
        /// </summary>
        public void Teleport(Vector2 position, bool cleanGravity = false) {
            Vector2 delta = position - m_CurrentPosition;
            m_PreviousPosition += delta;
            m_CurrentPosition = position;
            m_Rigidbody2D.MovePosition(position);

            /// 清除y方向的速度

            if (cleanGravity) {
                m_Velocity.y = 0;
            }
        }

        void FixedUpdate() {

#if UNITY_EDITOR
            CaculateJumpSpeed();
#endif

            /// 记录当前位置 然后根据移动距离计算当前位置

            m_PreviousPosition = m_Rigidbody2D.position;
            m_CurrentPosition = m_PreviousPosition + m_NextMovement;

            if (LockDirection) {

                /// 暂时为推拉状态开绿色通道

                if (CurrentState != null && (CurrentState as CharacterStatePush) != null) {

                    m_Velocity.x = m_CharacterInput.Horizontal * runSpeed;
                } else {

                    m_Velocity.x = 0f;

                }


            } else {

                /// 左右移动速度计算

                m_Velocity.x = m_CharacterInput.Horizontal * runSpeed;

            }

            /// 到达顶部时 清除跳跃速度

            if (IsCeiling && m_Velocity.y > 0f) {
                m_Velocity.y = 0;
            }

            if (IsGround) {

                /// 踩在角色头上时禁止/清除重力

                m_DisableGravity = CheckDisableGravity(typeof(CharacterController2D));

            } else {

                m_DisableGravity = false;
            }

            /// 禁止/清除重力

            if (m_DisableGravity && m_Velocity.y > 0f) {
                m_Velocity.y = 0f;
            }

            StopJump();

            /// 在地面时 按下跳跃键起跳

            if (CanJump()) {
                StartJump();
            }

            if (!IsGround && !LockMovement) {

                /// 角色出于空中时的重力计算

                var realGravity = gravity;
                if (m_CharacterInput.IsPressedJump && m_Velocity.y > 0) {
                    realGravity -= Mathf.Clamp(m_GravityIgnore, 0, gravity);
                }

                m_Velocity.y -= realGravity * Time.fixedDeltaTime;

            } else {

                /// 角色出于地面时的重力计算

                m_Velocity.y = -gravity * Time.fixedDeltaTime;
            }

            /// 根据移动速度计算当前位置

            m_CurrentPosition += m_Velocity * Time.fixedDeltaTime;

            /// 跳跃开始时提升与射线距离相同的高度以防止射线检测到地面

            if (m_JumpInThisFrame) {
                m_CurrentPosition += Vector2.up * raycastDistance;
            }

            /// 到达顶部时减少与射线距离相同的高度以防止射线检测到顶部

            if (IsCeiling) {
                m_CurrentPosition += Vector2.down * raycastDistance;
            }

            if (LockMovement) {
                m_CurrentPosition = m_Rigidbody2D.position;
            }

            /// 通过刚体组件来移动位置

            m_Rigidbody2D.MovePosition(m_CurrentPosition);
            m_NextMovement = Vector2.zero;

            /// 射线/碰撞检测

            CheckCollisions();

            /// 翻转检测

            if (!LockDirection) {
                if (CanFlipX()) {
                    FlipX();
                }
            }


            /// 更新动画控制器的参数
            UpdateAnimatorParameters();
        }

        bool CheckDisableGravity(params System.Type[] types) {
            foreach (var c in m_GroundColliders) {
                foreach (var t in types) {
                    var other = c.GetComponent(t);
                    if (other != null && other != this) {
                        return true;
                    }
                }
            }
            return false;
        }

        void UpdateAnimatorParameters() {
            m_Animator.SetBool("IsGround", IsGround);
            m_Animator.SetFloat("Horizontal", m_CharacterInput.Horizontal);
            m_Animator.SetBool("IsRunning", !Mathf.Approximately(m_CharacterInput.Horizontal, 0f));
        }

        bool CanJump() {

            return IsGround && m_CharacterInput.IsPressedJump;
        }

        bool m_JumpInThisFrame;

        void StartJump() {
            m_Velocity.y = m_JumpSpeed;
            m_JumpInThisFrame = true;
            IsGround = false;
        }

        void StopJump() {
            m_JumpInThisFrame = false;
        }

        bool CanFlipX() {
            return (IsLeft && m_CharacterInput.IsRunRight) || (!IsLeft && m_CharacterInput.IsRunLeft);
        }

        void FlipX() {
            m_CurrentPosition = m_BoxCollider2D.FlipX();
            m_Rigidbody2D.MovePosition(m_CurrentPosition);
            IsLeft = !IsLeft;
        }

        bool RaycastAll(List<Collider2D> colliders, RaycastDirection direction, LayerMask layerMask) {
            colliders.Clear();
            var list = m_BoxCollider2D.RaycastAll(direction, layerMask, raycastDistance, raycastCount);
            foreach (var c in list) {
                if (c != m_BoxCollider2D) {
                    colliders.Add(c);
                }
            }
            return colliders.Count > 0;
        }

        /// <summary>
        /// 射线检测碰撞
        /// </summary>
        public void CheckCollisions() {
            //bool queriesStartInColliders = Physics2D.queriesStartInColliders;
            //Physics2D.queriesStartInColliders = false;
            IsGround = RaycastAll(m_GroundColliders, RaycastDirection.Down, groundLayers);
            IsCeiling = RaycastAll(m_CeilingColliders, RaycastDirection.Up, ceilingLayers);
            //Physics2D.queriesStartInColliders = queriesStartInColliders;
        }

    }

}
